format ELF64 executable 3
entry start

macro emit [param] { param }

segment readable executable

; in: edi - alloc size in bytes
; out: rax - pointer to allocated memory
alloc_mem:
    mov eax,9           ; sys_map
    xor esi,esi         ; addr
    xchg esi,edi        ; length (esi)
    mov edx,0x1+0x2     ; prot = PROT_READ | PROT_WRITE
    mov r10d,0x02+0x20  ; flags = MAP_PRIVATE | MAP_ANONYMOUS
    mov r8,-1           ; fd
    xor r9d,r9d         ; offset
    syscall
    ret

; in: xmm0 - angle in radian
; out: xmm0 - sin(xmm0.x)
am_sin_ss:
    movaps xmm7,xmm0
    movss xmm1,[i32_inv_sign_mask]
    movss xmm2,[i32_sign_mask]
    movss xmm3,[f32_2_div_pi]
    andps xmm0,xmm1
    andps xmm7,xmm2
    mulss xmm0,xmm3
    pxor xmm3,xmm3
    movd xmm5,[i32_1]
    movss xmm4,[v4_f32_1]
    cvttps2dq xmm2,xmm0
    pand xmm5,xmm2
    movd xmm1,[i32_2]
    pcmpeqd	xmm5,xmm3
    cvtdq2ps xmm6,xmm2
    pand xmm2,xmm1
    pslld xmm2,30
    subss xmm0,xmm6
    movss xmm3,[f32_sincos_p3]
    minss xmm0,xmm4
    subss xmm4,xmm0
    andps xmm0,xmm5
    andnps xmm5,xmm4
    orps xmm0,xmm5
    movaps xmm1,xmm0
    movss xmm4,[f32_sincos_p2]
    mulss xmm0,xmm0
    xorps xmm2,xmm7
    movss xmm5,[f32_sincos_p1]
    orps xmm1,xmm2
    movaps xmm7,xmm0
    mulss xmm0,xmm3
    movss xmm6,[f32_sincos_p0]
    addss xmm0,xmm4
    mulss xmm0,xmm7
    addss xmm0,xmm5
    mulss xmm0,xmm7
    addss xmm0,xmm6
    mulss xmm0,xmm1
    ret

start:
    emit <mov rbp,rsp>,<sub rsp,128>
    emit <fb equ rbp-8>,<img equ rbp-16>,<ix equ rbp-20>,<iy equ rbp-24>
    ; alloc memory for the framebuffer (RGBA32F)
    emit <mov edi,FB_W*FB_H*16>,<call alloc_mem>,<mov [fb],rax>

    mov rbx,[fb]
    mov dword [iy],0
      .for_each_y:
    mov dword [ix],0

      .for_each_x:
    ; convert to [-1.0;1.0]
    emit <cvtsi2ss xmm0,[ix]>,<divss xmm0,[f32_fb_w]>,<subss xmm0,[f32_0p5]>,<addss xmm0,xmm0>
    ; compute sine and convert to [0.0;1.0]
    emit <mulss xmm0,[f32_10]>,<call am_sin_ss>,<mulss xmm0,[f32_0p5]>,<addss xmm0,[f32_0p5]>,<movss [rbx+8],xmm0>

    ; convert to [-1.0;1.0]
    emit <cvtsi2ss xmm0,[iy]>,<divss xmm0,[f32_fb_h]>,<subss xmm0,[f32_0p5]>,<addss xmm0,xmm0>
    ; compute sine and convert to [0.0;1.0]
    emit <addss xmm0,[f32_0p5]>,<mulss xmm0,[f32_10]>,<call am_sin_ss>,<mulss xmm0,[f32_0p5]>,<addss xmm0,[f32_0p5]>,<movss [rbx+4],xmm0>

    add rbx,16

    emit <inc dword [ix]>,<cmp dword [ix],FB_W>,<jne .for_each_x>
    emit <inc dword [iy]>,<cmp dword [iy],FB_H>,<jne .for_each_y>

    ; alloc memory for the image (RGBA8)
    emit <mov edi,FB_W*FB_H*4>,<call alloc_mem>,<mov [img],rax>
    mov ecx,FB_W*FB_H
    pxor xmm1,xmm1
    ; set dst and src pointers
    emit <mov rdi,[img]>,<mov rsi,[fb]>
      @@:
    ; clamp to [0.0; 1.0]
    emit <movaps xmm0,[rsi]>,<maxps xmm0,xmm1>,<minps xmm0,dqword [v4_f32_1]>
    ; convert from [0.0; 1.0] to [0; 255]
    emit <mulps xmm0,dqword [v4_f32_255]>,<cvttps2dq xmm0,xmm0>
    pshufb xmm0,dqword [v16_i8_image_conv_mask]
    emit <movd [rdi],xmm0>,<or dword [rdi],0xff000000>
    ; continue loop
    emit <add rsi,16>,<add rdi,4>,<dec ecx>,<jnz @b>

    ; create TGA file
    emit <mov eax,85>,<mov rdi,image_name>,<mov esi,110000000b>,syscall,<mov rbx,rax>
    ; write TGA header
    emit <mov eax,1>,<mov rdi,rbx>,<mov rsi,image_header>,<mov edx,18>,syscall
    ; write TGA pixel data
    emit <mov eax,1>,<mov rdi,rbx>,<mov rsi,[img]>,<mov edx,FB_W*FB_H*4>,syscall

    ; terminate process
    emit <mov eax,60>,<xor edi,edi>,syscall

align 1
image_name db 'nolib_sin.tga',0
image_header db 0,0,2,9 dup 0,(FB_W and 0x00ff),(FB_W and 0xff00) shr 8,(FB_H and 0x00ff),(FB_H and 0xff00) shr 8,32,0

align 4
i32_sign_mask dd 0x80000000
i32_inv_sign_mask dd not 0x80000000
f32_2_div_pi dd 0.636619772
i32_1 dd 1
i32_2 dd 2
f32_10 dd 10.0
f32_0p5 dd 0.5
FB_W = 800
FB_H = 600
f32_fb_w dd 800.0
f32_fb_h dd 600.0

f32_sincos_p0 dd 1.5707963267948963959
f32_sincos_p1 dd -0.64596409750621907082
f32_sincos_p2 dd 0.07969262624561800806
f32_sincos_p3 dd -0.00468175413106023168

align 16
v16_i8_image_conv_mask db 0, 4, 8, 12, 12 dup 0x80
v4_f32_1 dd 4 dup 1.0
v4_f32_255 dd 4 dup 255.0
v4_f32_red dd 0.0,0.8,1.0,0.0
